﻿using System;
using System.Collections.Generic;
using System.Text;

namespace TTLL.Asm
{
    public class ToText
    {
        public const string tagVariabe = "@";
        public const string tagParam = "#";
        public const string tagConst = "$";
        public static uint GetUInt32FromBytes(byte[] data)
        {
            if (data.Length == 0)
                return 0;
            else if (data.Length == 1)
                return (UInt32)data[0];
            else if (data.Length == 2)
                return (UInt32)data[0] + (UInt32)data[1] * 0x100;
            else if (data.Length == 3)
                return (UInt32)data[0] + (UInt32)data[1] * 0x100 + (UInt32)data[2] * 0x10000;
            else if (data.Length == 4)
                return BitConverter.ToUInt32(data, 0);
            else
                throw new Exception("error length");
        }
        public static UInt64 GetUInt64FromBytes(byte[] data)
        {
            if (data.Length <= 4)
                return GetUInt32FromBytes(data);
            else if (data.Length == 5)
                return GetUInt32FromBytes(data) + (UInt64)data[4] * 0x100000000;
            else if (data.Length == 6)
                return GetUInt32FromBytes(data) + (UInt64)data[4] * 0x100000000 + (UInt64)data[5] * 0x10000000000;
            else if (data.Length == 7)
                return GetUInt32FromBytes(data) + (UInt64)data[4] * 0x100000000 + (UInt64)data[5] * 0x10000000000 + (UInt64)data[6] * 0x1000000000000;
            else if (data.Length == 8)
                return BitConverter.ToUInt64(data, 0);
            else
                throw new Exception("error length");
        }
        public static Dictionary<string, string> SaveModuleToString(TTLL.Asm.Module module, string splitfunctag = "::")
        {
            ////根据名字里有几个分割标志,分成不同的组
            //List<List<string>> funcDeepGourp = new List<List<string>>();
            //foreach (var func in module.funcNameTable)
            //{
            //    var funcstep = func.Split(new string[] { splitfunctag }, StringSplitOptions.None).Length - 1;
            //    while (funcDeepGourp.Count <= funcstep)
            //    {
            //        funcDeepGourp.Add(new List<string>());
            //    }
            //    funcDeepGourp[funcstep].Add(func);
            //}

            //然后根据前面的东西分组，等下，那好像不需要深度分割组...
            Dictionary<string, List<string>> finalGroup = new Dictionary<string, List<string>>();
            Dictionary<string, List<string>> finalStructGroup = new Dictionary<string, List<string>>();

            foreach (var func in module.funcNameTable)
            {
                var indexspilit = func.LastIndexOf(splitfunctag);
                if (indexspilit < 0)
                {
                    if (finalGroup.ContainsKey(splitfunctag) == false)
                        finalGroup[splitfunctag] = new List<string>();

                    finalGroup[splitfunctag].Add(func);
                }
                else
                {
                    var frontpart = func.Substring(0, indexspilit);

                    if (finalGroup.ContainsKey(frontpart) == false)
                        finalGroup[frontpart] = new List<string>();

                    finalGroup[frontpart].Add(func);
                }
            }

            foreach (var _struct in module.structNameTable)
            {
                var indexspilit = _struct.LastIndexOf(splitfunctag);
                if (indexspilit < 0)
                {
                    if (finalStructGroup.ContainsKey(splitfunctag) == false)
                        finalStructGroup[splitfunctag] = new List<string>();

                    finalStructGroup[splitfunctag].Add(_struct);
                }
                else
                {
                    var frontpart = _struct.Substring(0, indexspilit);

                    if (finalStructGroup.ContainsKey(frontpart) == false)
                        finalStructGroup[frontpart] = new List<string>();

                    finalStructGroup[frontpart].Add(_struct);
                }
            }
            List<string> allkey = new List<string>();
            foreach (var group in finalGroup.Keys)
            {
                if (allkey.Contains(group) == false)
                    allkey.Add(group);
            }
            foreach (var group in finalStructGroup.Keys)
            {
                if (allkey.Contains(group) == false)
                    allkey.Add(group);
            }
            Dictionary<string, string> outstrs = new Dictionary<string, string>();
            foreach (var key in allkey)
            {
                List<string> structgroup = finalStructGroup.ContainsKey(key) ? finalStructGroup[key] : null;
                List<string> funcgroup = finalGroup.ContainsKey(key) ? finalGroup[key] : null;
                var filekey = key;
                if (filekey == splitfunctag)
                    filekey = "__main.asm";
                else
                    filekey = key.Replace(splitfunctag, "_") + ".asm";
                var text = SaveFuncGroupToString(module, structgroup, funcgroup);

                outstrs.Add(key, text);
            }

            return outstrs;
        }
        static string SaveFuncGroupToString(TTLL.Asm.Module module, List<string> structgroup, List<string> funcgroup)
        {
            StringBuilder finalString = new StringBuilder();
            //coll struct
            Dictionary<string, Struct> structs = new Dictionary<string, Struct>();
            if (structgroup != null)
            {
                foreach (var _struct in structgroup)
                {
                    var structobj = module.structTable[module.structNameTable.IndexOf(_struct)];
                    structs.Add(_struct, structobj);
                }
            }
            //coll function
            Dictionary<string, Function> funcs = new Dictionary<string, Function>();
            if (funcgroup != null)
            {
                foreach (var func in funcgroup)
                {
                    var funcobj = module.funcTable[module.funcNameTable.IndexOf(func)];
                    funcs.Add(func, funcobj);
                }
            }

            //coll touch consts
            List<uint> constref = CollConsts(structs, funcs);
            finalString.AppendLine("//===consts===");
            foreach (var cr in constref)
            {
                if (cr == 0) continue;//const 0 固定为 0
                var constitem = module.constTable[(int)cr - 1];
                string name = null;
                if (module.constNameTable != null)
                    name = module.constNameTable[(int)cr - 1];

                finalString.AppendLine(GetConstString(constitem, cr, name) + ";");
            }
            //struct
            foreach (var _struct in structs)
            {
                AppendStruct(finalString, module, _struct.Key, _struct.Value);

            }
            //functions
            foreach (var func in funcs)
            {
                AppendFunction(finalString, module, func.Key, func.Value);
            }
            return finalString.ToString();
        }
        static void AppendAttribute(StringBuilder stringouput, Module module, Attribute attr)
        {
            stringouput.Append("[");
            stringouput.Append(attr.name);
            if (attr.values != null && attr.values.Count > 0)
            {//有参数
                stringouput.Append("(");
                Struct _struct = null;
                if (module.structNameTable != null)
                {
                    var isturct = module.structNameTable.IndexOf(attr.name);
                    if (isturct >= 0)
                    {
                        _struct = module.structTable[isturct];
                        if (_struct.memberNameTable != null && _struct.memberNameTable.Count == _struct.members.Count)
                        {
                            if (attr.values.Count <= _struct.members.Count)
                            {

                            }
                            else
                            {
                                _struct = null;
                            }
                        }
                        else
                        {
                            _struct = null;
                        }
                    }
                }
                if (_struct != null) //if have struct，用命名参数形式
                {
                    for (var i = 0; i < attr.values.Count; i++)
                    {
                        if (i != 0) stringouput.Append(" , ");
                        var name = _struct.memberNameTable[i];
                        if (string.IsNullOrEmpty(name))
                        {
                            name = tagVariabe + i;
                        }
                        stringouput.Append(name + " = " + attr.values[i]);
                    }
                }
                else
                {
                    for (var i = 0; i < attr.values.Count; i++)
                    {
                        if (i != 0) stringouput.Append(" , ");
                        stringouput.Append(attr.values[i]);
                    }
                }
                stringouput.Append(")");
            }
            stringouput.AppendLine("]");
        }
        static void AppendStruct(StringBuilder stringouput, Module module, string name, Struct _struct)
        {
            if (_struct.attributes != null)
            {
                foreach (var att in _struct.attributes)
                {
                    AppendAttribute(stringouput, module, att);
                }
            }
            stringouput.AppendLine("struct " + name);
            stringouput.AppendLine("{");
            for (var i = 0; i < _struct.members.Count; i++)
            //m in _struct.members)
            {
                string mname = tagVariabe + (i + 1).ToString();
                string comment = "";
                if (_struct.memberNameTable.Count > 0)
                {
                    mname = _struct.memberNameTable[i];
                    if (string.IsNullOrEmpty(mname))
                    {
                        mname = tagVariabe + (i + 1).ToString();
                    }
                    else
                    {
                        comment = tagVariabe + (i + 1).ToString();
                    }
                }
                var member = _struct.members[i];
                stringouput.Append("    " + GetTypeString(member.type) + " " + mname);
                if (member.initConstValue > 0)
                {
                    stringouput.Append(" = " + GetConstNameShort(module, member.initConstValue));
                    comment += " = " + GetConstValueString(module.constTable[(int)member.initConstValue - 1]);
                }
                if (comment != "")
                    stringouput.AppendLine(";//" + comment);
                else
                    stringouput.AppendLine(";");
            }
            stringouput.AppendLine("}");

        }
        static void AppendFunction(StringBuilder stringouput, Module module, string name, Function func)
        {
            if (func.attributes != null)
            {
                foreach (var att in func.attributes)
                {
                    AppendAttribute(stringouput, module, att);
                }
            }
            stringouput.AppendLine("function " + GetTypeString(func.Return.type) + " " + name + "(" + GetParamsString(module, func) + ")");
            stringouput.AppendLine("{");
            for (var i = 0; i < func.Variabes.Count; i++)
            //(var v in func.Variabes)
            {
                //展示为虚拟指令 define
                string vname = null;
                string comment = "";
                if (func.varNameTable.Count > 0)
                {
                    vname = func.varNameTable[i];
                    if (string.IsNullOrEmpty(vname))
                    {
                        vname = tagVariabe + (i + 1).ToString();
                    }
                    else
                    {
                        comment = tagVariabe + (i + 1).ToString();
                    }
                }
                else
                {
                    vname = tagVariabe + (i + 1).ToString();
                }
                var type = GetTypeString(func.Variabes[i].type);

                if (func.Variabes[i].initConstValue > 0)
                {
                    var cid = func.Variabes[i].initConstValue;

                    comment += "=" + GetConstValueString(module.constTable[(int)cid - 1]);
                    //说明有初值
                    stringouput.Append("    Define " + type + " " + vname + " = " + GetConstNameShort(module, cid) + ";");
                }
                else
                {
                    stringouput.Append("    Define " + type + " " + vname + ";");
                }
                if (comment != "")
                    stringouput.AppendLine("//" + comment);
                else
                    stringouput.AppendLine();
            }
            for (var i = 0; i < func.Body.body.Count; i++)
            {
                //展示body指令
                AppendInstuction(stringouput, module, func, func.Body.body[i]);
            }
            stringouput.AppendLine("}");
        }
        static void AppendInstuction(StringBuilder stringouput, Module module, Function function, Instruction inst,bool enter=true)
        {
            //是否有返回值
            stringouput.Append("    ");
            var startindex = 0;
            var count = inst.instParams != null ? inst.instParams.Count : 0;
            if (count > 0 && inst.instParams[0].type == InstParamType.ReturnVar)
            {
                uint id = GetUInt32FromBytes(inst.instParams[0].value);
                if (id > 0)
                {
                    var returnstr = GetVarNameShort(function, inst.instParams[0].value) + " = ";
                    stringouput.Append(returnstr);
                }
                startindex = 1;
            }
            if (inst.code != OpCode.Block)
            {
                stringouput.Append(inst.code.ToString());
            }
            stringouput.Append(" ");
            for (var i = startindex; i < count; i++)
            {
                var instparam = inst.instParams[i];
                if (i != startindex)
                    stringouput.Append(" , ");
                AppendInstParam(stringouput, module, function, inst, instparam);
            }

            if (inst.code == OpCode.If)
            {
                stringouput.Append("(");
                AppendInstuction(stringouput, module, function, inst.IfCondition,false);
                stringouput.Append(")");
            }
            if (inst.code == OpCode.Block)
            {
                if(inst.Body.BlockLabel!=null)
                {
                    stringouput.Append(inst.Body.BlockLabel+":");
                }
                else
                {
                    stringouput.Append("_block"+inst.Body.BlockDepth + ":");

                }
            }
            if (inst.code == OpCode.Block || inst.code == OpCode.If)
            {
                stringouput.AppendLine("\n{");
                for (var i = 0; i < inst.Body.body.Count; i++)
                {
                    //展示body指令
                    AppendInstuction(stringouput, module, function, inst.Body.body[i]);
                }
                stringouput.AppendLine("}");
            }
            else if(enter)
            {
                stringouput.AppendLine(" ;");
            }
        }
        static string GetVarName(Function function, byte[] data)
        {
            uint id = GetUInt32FromBytes(data);
            if (function.varNameTable.Count > 0)
            {
                return function.varNameTable[(int)id - 1] + "/*" + tagVariabe + id + "*/";
            }
            else
            {
                return tagVariabe + id;
            }
        }
        static string GetParamName(Function function, UInt32 id)
        {
            if (function.paramNameTable.Count > 0)
            {
                var name = function.paramNameTable[(int)id - 1];
                if (string.IsNullOrEmpty(name))
                    return tagParam + id;
                else
                    return function.paramNameTable[(int)id - 1];
            }
            else
            {
                return tagParam + id;
            }
        }
        static string GetConstName(Module module, byte[] data)
        {
            uint id = GetUInt32FromBytes(data);
            if (module.constNameTable != null && module.constNameTable.Count > 0)
            {
                return module.constNameTable[(int)id - 1] + "/*" + tagConst + id + "*/";
            }
            else
            {
                return tagConst + id;
            }
        }
        static string GetVarNameShort(Function function, byte[] data)
        {
            uint id = GetUInt32FromBytes(data);
            if (function.varNameTable.Count > 0)
            {
                var name = function.varNameTable[(int)id - 1];
                if (string.IsNullOrEmpty(name))
                    return tagVariabe + id;
                else
                    return name;
            }
            else
            {
                return tagVariabe + id;
            }
        }
        static string GetParamNameShort(Function function, byte[] data)
        {
            uint id = GetUInt32FromBytes(data);
            if (function.paramNameTable.Count > 0)
            {
                return function.paramNameTable[(int)id - 1];
            }
            else
            {
                return tagParam + id;
            }
        }
        static string GetBlockName(Function function, byte[] data)
        {

            return data[0].ToString();
        }
        static string GetConstNameShort(Module module, UInt32 id)
        {
            if (module.constNameTable != null && module.constNameTable.Count > 0)
            {
                var name = module.constNameTable[(int)id - 1];
                if (string.IsNullOrEmpty(name))
                    return tagConst + id;
                else
                    return module.constNameTable[(int)id - 1];
            }
            else
            {
                return tagConst + id;
            }
        }
        static string GetConstNameShort(Module module, byte[] data)
        {
            uint id = GetUInt32FromBytes(data);
            if (module.constNameTable != null && module.constNameTable.Count > 0)
            {
                return module.constNameTable[(int)id - 1];
            }
            else
            {
                return tagConst + id;
            }
        }
        static void AppendInstParam(StringBuilder stringouput, Module module, Function function, Instruction inst, InstParam instparam)
        {
            if (instparam.type == InstParamType.Const)
            {
                var name = GetConstNameShort(module, instparam.value);
                stringouput.Append(name);
            }
            else if (instparam.type == InstParamType.Var)
            {
                var name = GetVarNameShort(function, instparam.value);
                stringouput.Append(name);
            }
            else if (instparam.type == InstParamType.RefVar)
            {
                var name = GetVarNameShort(function, instparam.value);
                stringouput.Append("ref " + name);
            }
            else if (instparam.type == InstParamType.ImNumber)
            {
                UInt64 num = GetUInt64FromBytes(instparam.value);
                stringouput.Append(num);
            }
            else if (instparam.type == InstParamType.Param)
            {
                var name = GetParamNameShort(function, instparam.value);
                stringouput.Append(name);
            }
            else if (instparam.type == InstParamType.BlockLayer)
            {
                var name = GetBlockName(function, instparam.value);
                stringouput.Append(name);
            }
            else
            {
                throw new Exception("unknown thing");
            }
        }
        static string GetTypeString(NativeTypeEX type)
        {
            return type.type.ToString().ToLower();
        }
        static string GetTypeString(NativeType type)
        {
            return type.ToString().ToLower();
        }
        static string GetParamsString(Module module, Function func)
        {
            var outstr = "";
            for (UInt32 i = 0; i < func.Params.Count; i++)
            {
                var param = func.Params[(int)i];
                if (i != 0)
                    outstr += " , ";

                outstr += GetParamString(module, func, param, i + 1);
            }
            return outstr;
        }
        static string GetParamString(Module module, Function func, VarDefine param, UInt32 _paramid)
        {

            var outstr = GetTypeString(param.type) + " " + GetParamName(func, _paramid);
            string comment = "";
            if (func.paramNameTable.Count > 0)
            {
                comment += tagParam + _paramid;
            }

            if (param.initConstValue > 0)
            {
                var constv = module.constTable[(int)param.initConstValue - 1];
                outstr += " = " + GetConstNameShort(module, param.initConstValue);
                comment += " = " + GetConstValueString(constv);
            }
            if (comment != "")
                outstr += "/*" + comment + "*/";
            return outstr;
        }
        static string GetConstString(ConstValue constv, UInt32 index, string name = null)
        {
            var typestr = GetConstTypeString(constv);
            var valuestr = GetConstValueString(constv);

            if (string.IsNullOrEmpty(name))
                return "const " + typestr + " " + tagConst + index + " = " + valuestr + ";";
            else
                return "const " + typestr + " " + name + " = " + valuestr + ";" + "//" + tagConst + index;
        }
        static string GetConstTypeString(ConstValue constv)
        {
            return constv.type.type.ToString();
        }
        static string GetConstValueString(ConstValue constv)
        {
            if (constv.type.type == NativeType.Byte8 || constv.type.type == NativeType.Ptr64)
                return BitConverter.ToUInt64(constv.data, 0).ToString();
            else if (constv.type.type == NativeType.Byte4 || constv.type.type == NativeType.Ptr32)
                return BitConverter.ToUInt32(constv.data, 0).ToString();
            else if (constv.type.type == NativeType.Byte2)
                return BitConverter.ToUInt16(constv.data, 0).ToString();
            else if (constv.type.type == NativeType.Byte1)
                return constv.data[0].ToString();
            else if (constv.type.type == NativeType.ByteArray_String_ASCII_ZeroEnd)
            {
                return System.Text.Encoding.ASCII.GetString(constv.data, 0, constv.data.Length - 1);
            }
            else if (constv.type.type == NativeType.ByteArray_String_UTF8_ZeroEnd)
            {
                return System.Text.Encoding.UTF8.GetString(constv.data, 0, constv.data.Length - 1);
            }
            else if (constv.type.type == NativeType.ByteArray_String_UTF16_ZeroEnd)
            {
                return System.Text.Encoding.Unicode.GetString(constv.data, 0, constv.data.Length - 1);
            }
            else if (constv.type.type == NativeType.ByteArray_String_UTF32_ZeroEnd)
            {
                return System.Text.Encoding.UTF32.GetString(constv.data, 0, constv.data.Length - 1);
            }
            else
            {
                var str = "[";
                for (var i = 0; i < constv.data.Length; i++)
                {
                    if (i != 0)
                        str += ",";
                    str += constv.data[i].ToString("X02");
                }
                return str;
            }

        }
        private static List<uint> CollConsts(Dictionary<string, Struct> structs, Dictionary<string, Function> funcs)
        {
            List<UInt32> constref = new List<uint>();
            foreach (var _structs in structs.Values)
            {
                foreach (var m in _structs.members)
                {
                    if (constref.Contains(m.initConstValue) == false)
                        constref.Add(m.initConstValue);
                }
            }
            foreach (var func in funcs.Values)
            {
                //参数
                foreach (var p in func.Params)
                {
                    if (constref.Contains(p.initConstValue) == false)
                        constref.Add(p.initConstValue);
                }
                //临时变量
                foreach (var p in func.Variabes)
                {
                    if (constref.Contains(p.initConstValue) == false)
                        constref.Add(p.initConstValue);
                }
                //返回值，掠过（返回值不可能包含conset）
                //代码
                foreach (var inst in func.Body.body)
                {

                    foreach (var p in inst.instParams)
                    {
                        if (p.type == InstParamType.Const)
                        {
                            var cr = GetUInt32FromBytes(p.value);
                            if (constref.Contains(cr) == false)
                                constref.Add(cr);
                        }
                    }

                }
            }

            return constref;
        }
    }
}
